home *** CD-ROM | disk | FTP | other *** search
- /*
- ** File mfile.c
- ** Source file for MultiFile dialog.
- **
- ** Copyright © Eric Schlegel 1987, 1988
- */
-
-
- #include <Types.h>
- #include <Resources.h>
- #include <memory.h>
- #include <QuickDraw.h>
- #include <Fonts.h>
- #include <Windows.h>
- #include <Menus.h>
- #include <TextEdit.h>
- #include <Dialogs.h>
- #include <Events.h>
- #include <Lists.h>
- #include <Packages.h>
- #include <Controls.h>
- #include <Files.h>
- #include <Strings.h>
- #include <osutils.h>
- #include <string.h>
- #include "mfpriv.h"
-
-
- #define INITMSG (-1) /* msg to dlgHook to init itself */
- #define NULLMSG (100) /* msg saying nothing happened */
- #define DIRMSG (102) /* msg saying hit in cur dir popup */
- #define FOLDMSG (103) /* msg to open a folder */
- #define GETDISK (4) /* item number of disk name */
- #define DONE_BTN (11) /* "Done" button item number */
- #define ADD_BTN (12) /* "Add" button item number */
- #define RM_BTN (13) /* "Remove" button item number */
- #define LST_ITEM (14) /* my file list item number */
- #define PROMPT_ITEM (15) /* prompt stattext item */
-
- #define SCRLSIZE (15) /* size of a scroll bar */
-
- #define LDEFID (128) /* res id of ldef */
- #define WDALRTID (3999) /* res id of working dir alert */
- #define MFDLGID (4000) /* res id of multifile dialog */
- #define MFSTRSID (512) /* res id of str# resource */
-
- #define ACTIVE (0) /* constants for hilitebtn */
- #define INACTIVE (255)
-
- #define ENTER (0x03) /* charcode for the Enter key */
-
- #define FSFCBLen (*((short *)(0x3f6))) /* -1 if mfs, >0 if hfs */
- #define SFSaveDisk (*((short *)(0x214))) /* -current vRefNum */
- #define CurDirStore (*((long *)(0x398))) /* current dirID */
-
-
- typedef struct fstruct {
- OSType ftype; /* the file type */
- short vrefnum; /* a vrefnum (or wdrefnum) for the file */
- short fver; /* the file's version */
- String(63) fname; /* the file's name in Pascal form */
- } FSTRUCT;
-
- typedef FSTRUCT (**FHNDL)[32767]; /* handle to an array of FSTRUCTs */
-
-
- static int nfiles; /* number of files */
- static FHNDL files; /* handle to array of files */
- static DialogPtr mfdlg; /* the multifile dialog */
- static ListHandle list; /* my file name list */
- static SFReply reply; /* dummy reply struct for SFPGetFile */
- static char myprompt[256]; /* my internal copy of the prompt */
- static Boolean is_add; /* true if Add button, false if Open button */
- static Boolean chklist; /* true if we should check SF's selection */
- static Boolean good; /* true if user hit done, false if cancel */
- static char openstr[256]; /* "Open" */
- static char addstr[256]; /* "Add" */
-
-
-
- /* center r in the screen 1/hfract across and 1/vfract down */
- static void center(r, hfract, vfract)
- Rect *r;
- int hfract;
- int vfract;
- {
- int h;
- int v;
-
- h = qd.screenBits.bounds.right - qd.screenBits.bounds.left;
- v = qd.screenBits.bounds.bottom - qd.screenBits.bounds.top;
-
- h = h - r->right + r->left;
- v = v - r->bottom + r->top;
-
- h = h / hfract;
- v = v / vfract;
-
- SetRect(r, h, v, h + r->right - r->left, v + r->bottom - r->top);
- }
-
- /* calculate the top left corner of the dialog */
- void get_tl(where)
- Point *where;
- {
- DialogTHndl dlgres;
- Rect drect;
-
- /* use default vals for classic mac screen if we can't get dialog */
- if (!(dlgres = GetResource('DLOG', MFDLGID))) {
- where->h = 82;
- where->v = 33;
- return;
- }
-
- drect = (*dlgres)->boundsRect;
- center(&drect, 2, 3);
- if (drect.top < 30) /* keep the top of the dialog at least 10 */
- drect.top = 30; /* pixels below the menu bar. */
-
- where->h = drect.left;
- where->v = drect.top;
- }
-
- /* start up the multifile dialog */
- Boolean multifile(where, prompt, filefilt, numtypes, typelist)
- Point *where;
- char *prompt;
- ProcPtr filefilt;
- int numtypes;
- SFTypeList *typelist;
- {
- pascal short dlg_hook();
- pascal Boolean filter();
-
- /* make a copy of the prompt so initdlg() can access it */
- strcpy(myprompt, prompt);
-
- GetIndString (openstr, MFSTRSID, 1);
- if (strlen(openstr) == 0)
- strcpy(openstr, "Open");
-
- GetIndString (addstr, MFSTRSID, 2);
- if (strlen(addstr) == 0)
- strcpy(addstr, "Add");
-
- is_add = true;
- chklist = true;
-
- SFPGetFile(where, '', filefilt, numtypes, typelist, dlg_hook, &reply, MFDLGID, filter);
-
- return(good);
- }
-
- /* return number of files collected */
- int numfiles()
- {
- return(nfiles);
- }
-
- /* return file #fileno in fstrct */
- void getfile(fileno, fstrct)
- int fileno; /* fileno is 1-based */
- FSTRUCT *fstrct;
- {
- BlockMove(&(**files)[fileno-1], fstrct, sizeof(FSTRUCT));
- }
-
- /* dispose of files memory */
- void freemf()
- {
- /* make sure we've got a valid handle */
- if (files)
- DisposHandle(files);
- }
-
- /************************************************************************
- utility routines
- ************************************************************************/
- /* flashes the button item #itemno in dlg for 4 ticks */
- static void flashbtn(dlg, itemno)
- DialogPtr dlg;
- int itemno;
- {
- short itemtype;
- Handle item;
- Rect box;
- long finalticks;
-
- GetDItem(dlg, itemno, &itemtype, &item, &box);
-
- HiliteControl(item, inButton);
- Delay(4, &finalticks);
- HiliteControl(item, 0);
- }
-
- /* returns the item rect of item #itemno in dlg */
- static void getitemr(dlg, itemno, r)
- DialogPtr dlg;
- int itemno;
- Rect *r;
- {
- short itemtype;
- Handle item;
-
- GetDItem(dlg, itemno, &itemtype, &item, r);
- }
-
- /* calls ValidRect for the rect enclosing item #itemno in dlg. */
- static void validitem(dlg, itemno)
- DialogPtr dlg;
- int itemno;
- {
- Rect itemr;
- GrafPtr oldport;
-
- /* make sure the port is correct before doing the ValidRect */
- GetPort(&oldport);
- SetPort(dlg);
-
- getitemr(dlg, itemno, &itemr);
- ValidRect(&itemr);
-
- SetPort(oldport);
- }
-
- /* returns the hiliting of the button item #itemno in dlg. */
- static int gethilite(dlg, itemno)
- DialogPtr dlg;
- int itemno;
- {
- short itemtype;
- Handle item;
- Rect box;
-
- GetDItem(dlg, itemno, &itemtype, &item, &box);
- return((*(ControlHandle)item)->contrlHilite);
- }
-
- /* sets the hiliting of the button item #itemno in dlg to state. */
- static void hilitebtn(dlg, itemno, state)
- DialogPtr dlg;
- int itemno;
- int state;
- {
- short itemtype;
- Handle item;
- Rect box;
-
- GetDItem(dlg, itemno, &itemtype, &item, &box);
- HiliteControl(item, state);
- }
-
- /* sets the title of the Add button to title. */
- static void set_title(dlg, title)
- DialogPtr dlg;
- char *title;
- {
- short itemtype;
- Handle item;
- Rect box;
-
- GetDItem(dlg, ADD_BTN, &itemtype, &item, &box);
- SetCTitle(item, title);
- }
-
- /* the tests used in noselect(), isfolder(), and isfile() come from */
- /* "C Workshop" in the April '86 MacTutor. */
-
- /* returns true if there's no selection in SF's list, false if there is */
- static Boolean noselect()
- {
- return((reply.fType == 0) && (reply.fName.length == 0));
- }
-
- /* returns true if a folder is selected in SF's list, false if not */
- static Boolean isfolder()
- {
- return((reply.fType > 0) && (reply.fName.length == 0));
- }
-
- /* returns true if a file is selected in SF's list, false if not */
- static Boolean isfile()
- {
- return((reply.fType > 0) && (reply.fName.length > 0));
- }
-
- /* if mfs, simply returns the current vrefnum; if hfs, makes the current */
- /* dir a working directory, and returns the wdrefnum, or 0 if the PBOpenWD */
- /* failed because too many wd's were already open. */
- static short makewd()
- {
- WDPBRec wdpb;
- HParamBlockRec hpb;
-
- /* if running MFS */
- if (FSFCBLen == -1)
- return(-SFSaveDisk);
-
- /* running HFS; check for an MFS volume; return vrefnum if so */
- hpb.volumeParam.ioCompletion = NULL;
- hpb.volumeParam.ioNamePtr = NULL;
- hpb.volumeParam.ioVRefNum = -SFSaveDisk;
- hpb.volumeParam.ioVolIndex = NULL;
- PBHGetVInfo(&hpb, false);
- if (hpb.volumeParam.ioVSigWord == 0xd2d7)
- return(-SFSaveDisk);
-
- /* make a new working directory */
- wdpb.ioCompletion = NULL;
- wdpb.ioNamePtr = NULL;
- wdpb.ioVRefNum = -SFSaveDisk;
- wdpb.ioWDProcID = 'ERIK';
- wdpb.ioWDDirID = CurDirStore;
-
- if (!PBOpenWD(&wdpb, false))
- return(wdpb.ioVRefNum);
- else
- return(0);
- }
-
- /* fill in an mfcell with data about a file */
- static void mkcell(thecell, fname, ftype, fver, vrefnum, dirid)
- MFCELL *thecell;
- String(63) *fname;
- OSType ftype;
- short fver;
- short vrefnum;
- long dirid;
- {
- int namelen;
-
- thecell->ftype = ftype;
- thecell->fver = fver;
- thecell->vrefnum = vrefnum;
- /* MFS doesn't use dirid's, so just use 0 instead */
- if (FSFCBLen > 0)
- thecell->dirid = dirid;
- else
- thecell->dirid = 0;
- namelen = fname->length;
- BlockMove(fname, &thecell->fname, namelen + 1);
- /* fill rest of fname with spaces */
- memset(thecell->fname.text + namelen, ' ', 63 - namelen);
- }
-
- /* returns true if the file currently in the reply rec is already in my */
- /* list, false if not. Also returns in thecell the cell that the file */
- /* is in, if it's in the list. */
- static Boolean inlist(thecell)
- Cell *thecell;
- {
- MFCELL thefile;
-
- /* build a cell with info about the current file */
- mkcell(&thefile, &reply.fName, reply.fType, reply.version, -SFSaveDisk, CurDirStore);
-
- /* look for the data */
- SetPt(thecell, 0, 0);
- return(LSearch(&thefile, sizeof(MFCELL), NULL, thecell, list));
- }
-
- /* unselects any selected cells, selects thecell, and scrolls to it */
- static void sel1cell(thecell)
- Cell *thecell;
- {
- Cell acell;
-
- /* unselect all selected cells. There must be a better way to do this. */
- SetPt (&acell, 0, 0);
- while (LGetSelect(true, &acell, list)) {
- LSetSelect(false, &acell, list);
- acell.v += 1;
- }
-
- LSetSelect(true, thecell, list);
- LAutoScroll(list);
- }
-
- /* adds fname to my list */
- static void addfile(fname, ftype, fver, vrefnum, dirid, wdrefnum)
- String(63) *fname;
- OSType ftype;
- short fver;
- short vrefnum;
- long dirid;
- short wdrefnum;
- {
- Cell thecell;
- MFCELL newfile;
-
- /* add a new row at the bottom of the list */
- LDoDraw(false, list);
- thecell.h = 0;
- thecell.v = LAddRow(1, (*list)->dataBounds.bottom, list);
- LDoDraw(true, list);
-
- /* build the new cell */
- mkcell(&newfile, fname, ftype, fver, vrefnum, dirid);
-
- /* add the new cell data */
- LSetCell(&newfile, sizeof(MFCELL), &thecell, list);
- thecell.h = 1;
- LSetCell(&wdrefnum, sizeof(short), &thecell, list);
-
- /* select the new cell */
- thecell.h = 0;
- sel1cell(&thecell);
- }
-
- /* remove file in thecell */
- static void rmfile(thecell)
- Cell *thecell;
- {
- LDelRow(1, thecell->v, list);
- }
-
- /************************************************************************
- dlog hook stuff
- ************************************************************************/
- static pascal short dlg_hook(item, dlg)
- short item;
- DialogPtr dlg;
- {
- void initdlg();
- void disposdlg();
- void chknmlist();
- void do_add();
- void do_rm();
-
- /* if a key is pressed, check SF's file list selection */
- if (item >= 1000) {
- chklist = true;
- return(item);
- }
-
- switch (item) {
- case INITMSG:
- initdlg(dlg);
- return(INITMSG);
- case ADD_BTN:
- if (isfile()) {
- do_add(dlg);
- return(ADD_BTN);
- } else { /* if we're not over a file, return */
- chklist = true; /* FOLDMSG since we're over a folder */
- return(FOLDMSG); /* and check the list next time */
- }
- case RM_BTN:
- do_rm(dlg);
- return(RM_BTN);
- case DONE_BTN:
- disposdlg();
- good = true; /* return getCancel here to make sure */
- return(getCancel); /* that SF doesn't make a wd for a file */
- case getOpen:
- do_add(dlg); /* item=getOpen on a double-click */
- return(NULLMSG); /* or a press of Return or Enter */
- case getCancel:
- disposdlg();
- good = false;
- return(getCancel);
- case getNmList:
- chknmlist(dlg);
- return(getNmList);
- case DIRMSG: /* for all of these, we want to */
- case FOLDMSG: /* check the file list next time */
- case GETDISK: /* and return the same item number */
- case getEject:
- case getDrive:
- chklist = true;
- return(item);
- default:
- return(item);
- }
- }
-
- /* init the dialog */
- static void initdlg(dlg)
- DialogPtr dlg;
- {
- pascal void userdraw();
-
- short itemtype;
- Handle item;
- Rect box;
- Rect bounds;
- Point csize;
-
- /* save dialogptr */
- mfdlg = dlg;
-
- /* set the prompt text */
- GetDItem(dlg, PROMPT_ITEM, &itemtype, &item, &box);
- SetIText(item, myprompt);
-
- /* get info about file list item and install user proc */
- GetDItem(dlg, LST_ITEM, &itemtype, &item, &box);
- SetDItem(dlg, LST_ITEM, itemtype, userdraw, &box);
-
- /* make file list */
- box.right -= SCRLSIZE;
- SetRect(&bounds, 0, 0, 2, 0);
- SetPt (&csize, box.right - box.left, 16);
- list = LNew(&box, &bounds, &csize, LDEFID, dlg, true, false, false, true);
-
- /* start with the Remove btn inactive, since there's nothing to remove */
- hilitebtn(dlg, RM_BTN, INACTIVE);
- }
-
- /* copy list info to files, then dispose of list */
- static void disposdlg()
- {
- Cell thecell;
- MFCELL data;
- short size;
- short wdrefnum;
- int i;
-
- nfiles = (*list)->dataBounds.bottom;
-
- /* if we can't get mem for file list, pretend there aren't any files */
- if (!(files = NewHandle(nfiles * sizeof(FSTRUCT))))
- nfiles = 0;
-
- /* copy info from list into files */
- for (i = 0; i < nfiles; i++) {
- /* get mfcell stuff from column 0 */
- thecell.h = 0;
- thecell.v = i;
- size = sizeof(MFCELL);
- LGetCell(&data, &size, &thecell, list);
-
- /* get wdrefnum from column 1 */
- thecell.h = 1;
- size = sizeof(short);
- LGetCell(&wdrefnum, &size, &thecell, list);
-
- (**files)[i].ftype = data.ftype;
- (**files)[i].vrefnum = wdrefnum;
- (**files)[i].fver = data.fver;
- BlockMove(&data.fname, &(**files)[i].fname, 64);
- }
-
- LDispose(list);
- }
-
- /* userproc to draw my file list and outline Add button */
- static pascal void userdraw(wp, item)
- WindowPtr wp;
- short item;
- {
- Rect box;
-
- chklist = true;
-
- LUpdate(wp->visRgn, list);
-
- /* frame file list */
- getitemr(wp, LST_ITEM, &box);
- box.right -= SCRLSIZE;
- InsetRect(&box, -1, -1);
- PenSize(1, 1);
- FrameRect(&box);
-
- /* outline Add button */
- getitemr(wp, ADD_BTN, &box);
- InsetRect(&box, -4, -4);
- PenNormal();
- PenSize(3, 3);
- FrameRoundRect(&box, 16, 16);
- }
-
- /* handle hits on the Add button */
- static void do_add(dlg)
- DialogPtr dlg;
- {
- void wdalert();
-
- short wdrefnum;
- Cell thecell;
-
- if (!inlist(&thecell)) {
- if (wdrefnum = makewd()) {
- addfile(&reply.fName, reply.fType, reply.version, -SFSaveDisk, CurDirStore, wdrefnum);
- hilitebtn(dlg, ADD_BTN, INACTIVE);
- hilitebtn(dlg, RM_BTN, ACTIVE);
- } else
- wdalert(dlg);
- }
- }
-
- /* positions the wd alert so that it's just above and inside */
- /* the sf dlg, and then displays it. */
- static void wdalert(dlg)
- DialogPtr dlg; /* the sfgetfile dlg */
- {
- int width; /* width of alert portrect */
- int height; /* height of alert portrect */
- AlertTHndl alrtres; /* handle to alert resource */
- Point botleft; /* botleft corner of dlg */
- GrafPtr oldport; /* save old port here */
-
- /* init botleft in local coords */
- botleft.h = 0;
- botleft.v = dlg->portRect.bottom;
-
- /* convert botleft to globals */
- GetPort(&oldport);
- SetPort(dlg);
- LocalToGlobal(&botleft);
- SetPort(oldport);
-
- /* set the alert portrect. If we can't get the resource, bag */
- /* the alert and just beep. */
- if (alrtres = GetResource('ALRT', WDALRTID)) {
- HNoPurge(alrtres);
- width = (*alrtres)->boundsRect.right - (*alrtres)->boundsRect.left;
- height = (*alrtres)->boundsRect.bottom - (*alrtres)->boundsRect.top;
- (*alrtres)->boundsRect.top = botleft.v - height - 10;
- (*alrtres)->boundsRect.left = botleft.h + 10;
- (*alrtres)->boundsRect.bottom = botleft.v - 10;
- (*alrtres)->boundsRect.right = botleft.h + width + 10;
-
- Alert(WDALRTID, NULL);
- } else
- SysBeep(5);
- }
-
- /* handle hits on the Remove button */
- static void do_rm(dlg)
- DialogPtr dlg;
- {
- void chknmlist();
-
- Cell thecell;
-
- SetPt(&thecell, 0, 0);
- while (LGetSelect(true, &thecell, list)) {
- rmfile(&thecell);
- SetPt(&thecell, 0, 0);
- }
-
- hilitebtn(dlg, RM_BTN, INACTIVE);
- chknmlist(dlg);
- }
-
- /* chknmlist is called after the selection in Standard File's list changes. */
- /* I check to see what the current selection is in SF's list and set the */
- /* title of my Add button appropriately. */
- static void chknmlist(dlg)
- DialogPtr dlg;
- {
- Rect btnrect;
- GrafPtr oldport;
- Cell thecell; /* which cell in my list has SF's selection */
- Boolean inmine; /* true if selected cell in SF's list is */
- /* also in mine */
-
- /* if there's no selection, unhilite the add button */
- /* if the selection is a folder, change the title of the Add */
- /* button to Open and make sure that it's active. */
- /* if the selection is a file, make sure that the title of the */
- /* Add button is Add and that it's active. */
- if (noselect())
- hilitebtn(dlg, ADD_BTN, INACTIVE);
- else if (isfolder()) {
- if (gethilite(dlg, ADD_BTN) == INACTIVE)
- hilitebtn(dlg, ADD_BTN, ACTIVE);
-
- if (is_add) {
- set_title(dlg, openstr);
-
- /* changing the title generates an unnecessary update event. */
- /* this gets rid of it. */
- validitem(dlg, ADD_BTN);
-
- is_add = false;
- }
- } else if (isfile()) {
- /* if the selected file in SF's list is also in my list, select */
- /* the corresponding cell in my list and hilite the rm button */
- if ((inmine = inlist(&thecell)) == true) {
- sel1cell(&thecell);
- hilitebtn(dlg, RM_BTN, ACTIVE);
- }
-
- /* if the file is in the list, make sure that the Add button is */
- /* inactive; otherwise, make sure that it's active. */
- if ((inmine) && (gethilite(dlg, ADD_BTN) == ACTIVE))
- hilitebtn(dlg, ADD_BTN, INACTIVE);
- else if ((!inmine) && (gethilite(dlg, ADD_BTN) == INACTIVE))
- hilitebtn(dlg, ADD_BTN, ACTIVE);
-
- if (!is_add) {
- set_title(dlg, addstr);
-
- /* bag the update event */
- validitem(dlg, ADD_BTN);
-
- is_add = true;
- }
- }
- }
-
- /************************************************************************
- filter stuff
- ************************************************************************/
- static pascal Boolean filter(dlg, event, itemhit)
- DialogPtr dlg;
- EventRecord *event;
- short *itemhit;
- {
- void do_keydown();
- void do_mousedown();
-
- if (chklist) {
- chknmlist(mfdlg);
- chklist = false;
- }
-
- switch (event->what) {
- case mouseDown:
- do_mousedown(dlg, event);
- return(false);
- case keyDown:
- case autoKey:
- do_keydown(dlg, event);
- return(false);
- default:
- return(false);
- }
- }
-
- /* handle mouseDowns and track mouse if in list box */
- static void do_mousedown(dlg, event)
- DialogPtr dlg;
- EventRecord *event;
- {
- Rect listr;
- Point locpt;
- GrafPtr oldport;
- Cell thecell;
-
- GetPort(&oldport);
- SetPort(dlg);
-
- getitemr(dlg, LST_ITEM, &listr);
-
- locpt = event->where;
- GlobalToLocal(&locpt);
- if (PtInRect(&locpt, &listr)) {
- LClick(&locpt, event->modifiers, list);
-
- /* hilite the remove button if there's a selection */
- SetPt(&thecell, 0, 0);
- if (LGetSelect(true, &thecell, list))
- hilitebtn(dlg, RM_BTN, ACTIVE);
- else
- hilitebtn(dlg, RM_BTN, INACTIVE);
- }
-
- SetPort(oldport);
- }
-
- /* flash Add/Open button if Return or Enter pressed */
- static void do_keydown(dlg, event)
- DialogPtr dlg;
- EventRecord *event;
- {
- int c;
- Cell thecell;
-
- /* only flash Add button if we're on a file that isn't in my list, */
- /* or if we're on a folder. */
- c = event->message & charCodeMask;
- if ((c == '\n') || (c == ENTER))
- if ((isfile() && !inlist(&thecell)) || isfolder())
- flashbtn(dlg, ADD_BTN);
- }